/****************************************************************************
 * Dirve lcd19264 , mode 8080. Tested on ABG192064C00-v1.0
 * Tested MCU MKE06Z
 * Owner : apleilx
 * 
 * Frequence : 40MHz
 * 
 * Some samble :
 * CN------------Chianese
 * Page----------The Page of LCD
 * Col-----------The col address of LCD
 * 16_16_________A char that is 16*16 dots
 * U-------------The chip select for LCD,(CS1--left / CS2--middle / CS3--right)
 * Com-----------LCD command
 * Disp----------Display
 * W-------------write
 * Rec-----------recently
 * Img-----------Image
 * Anti----------Anti-color
 * DIR-----------direction
 * DEL-----------delay
 * Explanation:
 * ANSEL must be set again
 * This file relay on ASCII_16_32.c,ASCII_8_16
 * that is in pic18f2xk22flash.h .
 * ASCII_16_32.c and ASCII_8_16 is unrelaied on hardware ,you can copy those to your project.
 * Imag.c and CN_16_16_Index.c is not necessary ,unless you want to display chianese and image.
 * **************************************************************************/

#include "lcd19264.h"
/**************************************************************************
 * define control bit for LCD19264
 *************************************************************************/
#define LCD_CS                          (3)
#define LCD_LINE_SIZE                   (192)
 
#define LCD_CS1_MASK                    (0x08000000UL)
#define LCD_CS2_MASK                    (0x00000100UL)
#define LCD_CS3_MASK                    (0x00000200UL)
#define LCD_E_MASK                      (0x04000000UL)
#define LCD_RW_MASK                     (0x00000008UL)
#define LCD_RS_MASK                     (0x00000004UL)
#define LCD_RST_MASK                    (0x10000000UL)
#define LCD_BUSY_MASK                   ((1UL << 23))
#define LCD_DATA_MASK                   (0xFFUL << 16)
#define LCD_DATA_POS                    (16)
 
#define LCD19264_CS1_Enable()           (FGPIOA->PCOR = LCD_CS1_MASK)
#define LCD19264_CS1_Disable()          (FGPIOA->PSOR = LCD_CS1_MASK) 

#define LCD19264_CS2_Enable()           (FGPIOB->PCOR = LCD_CS2_MASK)
#define LCD19264_CS2_Disable()          (FGPIOB->PSOR = LCD_CS2_MASK) 

#define LCD19264_CS3_Enable()           (FGPIOB->PCOR = LCD_CS3_MASK)
#define LCD19264_CS3_Disable()          (FGPIOB->PSOR = LCD_CS3_MASK) 

#define LCD19264_E(x)                   ((x)?(FGPIOA->PSOR = LCD_E_MASK):(FGPIOA->PCOR = LCD_E_MASK))
#define LCD19264_RW(x)                  ((x)?(FGPIOA->PSOR = LCD_RW_MASK):(FGPIOA->PCOR = LCD_RW_MASK))
#define LCD19264_RS(x)                  ((x)?(FGPIOA->PSOR = LCD_RS_MASK):(FGPIOA->PCOR = LCD_RS_MASK))
#define LCD19264_RESET(x)               ((x)?(FGPIOA->PSOR = LCD_RST_MASK):(FGPIOA->PCOR = LCD_RST_MASK))

#define LCD19264_BUSY()                 ( FGPIOB->PDIR & LCD_BUSY_MASK)     

#define LCD19264_DATA_IN()              FGPIOB->PDDR &= ~LCD_DATA_MASK              
#define LCD19264_DATA_OUT()             FGPIOB->PDDR |= LCD_DATA_MASK

/*****************************************************************************
 * DATA read and write
 ****************************************************************************/
 /*
__STATIC_INLINE uint8_t LCD19264_DATA_Read()
{	
	return (uint8_t)( (FGPIOB->PDIR >> LCD_DATA_POS) & 0xFF);
}
*/
__STATIC_INLINE void LCD19264_DATA_Write(uint8_t Data)
{
	FGPIOB->PCOR = LCD_DATA_MASK;
	FGPIOB->PSOR = (Data << LCD_DATA_POS);
}	

/*****************************************************************************
 *lcd E delay , 450ns
 ****************************************************************************/
__STATIC_INLINE void LCD19264_E_DEL()
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}

__STATIC_INLINE void LCD19264_Com_DEL()
{
	__nop();__nop();__nop();__nop();__nop();
}

__STATIC_INLINE void LCD19264_Sub_DEL()
{
	__nop();
}

/*****************************************************************************
 *lcd Ready delay , 350ns 
 ****************************************************************************/
__STATIC_INLINE void LCD19264_Read_DEL()
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}

__STATIC_INLINE void LCD19264_RESET_DEL()
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}

static unsigned char WaitTimeOutCnt;
static unsigned char LCDDataMask;
static FontType FontCur;
const unsigned char *AsciiImg;
static unsigned char FontSize;
static unsigned char bHalfSpace;

/****************************************************************************
 * The flowing function can be run independently.
 * Other functions rely on these functions.
 * so if you delete it, some functions may not work.
 ****************************************************************************/

void LCD19264_Busy(void);
void LCD19264_DataW(unsigned char Data);
void LCD19264_ComW(unsigned char Command);
void LCD19264_WriteCom_CS1(unsigned char Content);
void LCD19264_WriteCom_CS2(unsigned char Content);
void LCD19264_WriteCom_CS3(unsigned char Content);
void LCD19264_WriteCom(unsigned char U,unsigned char Content);

/******************************************************************************
 * Init
 * @param Content
 *****************************************************************************/
void LCD19264Init()
{
	 FGPIOA->PDDR |= LCD_CS1_MASK;
	 FGPIOB->PDDR |= LCD_CS2_MASK;
	 FGPIOB->PDDR |= LCD_CS3_MASK;
		
	 LCD19264_CS1_Disable();
	 LCD19264_CS2_Disable();
	 LCD19264_CS3_Disable();

	 FGPIOA->PDDR |= LCD_E_MASK;
	 LCD19264_E(0);
	 FGPIOA->PDDR |= LCD_RW_MASK;
	 LCD19264_RW(0);
	 FGPIOA->PDDR |= LCD_RS_MASK;
	 LCD19264_RS(0);

	 FGPIOB->PIDR &= ~LCD_DATA_MASK;
	 LCD19264_DATA_OUT();

	 FGPIOA->PDDR |= LCD_RST_MASK;
	 LCD19264_RESET(0);
	 LCD19264_RESET_DEL();
	 LCD19264_RESET(1);
	 LCD19264_RESET_DEL();
	 LCD19264_RESET(0);
	 LCD19264_RESET_DEL();
	 LCD19264_RESET(1);
	 //0x3E - display OFF
	 LCD19264_WriteCom_CS1(0x3E);  
	 LCD19264_WriteCom_CS2(0x3E);
	 LCD19264_WriteCom_CS3(0x3E);
	 //0x3F - display ON 
	 LCD19264_WriteCom_CS1(0x3F);   
	 LCD19264_WriteCom_CS2(0x3F);
	 LCD19264_WriteCom_CS3(0x3F);
	 //set start line
	 LCD19264_WriteCom_CS1(0xc0);  
	 LCD19264_WriteCom_CS2(0xc0);
	 LCD19264_WriteCom_CS3(0xc0);
	 
	 LCDDataMask = 0x00;
	 WaitTimeOutCnt = 0;
	 
	 LCD19264ScreenFill(0x00);
	 
	 LcdFontSet(FONT16);
}

/******************************************************************************
 * Lcd Font Set
 * @param Content
 *****************************************************************************/
void LcdFontSet(FontType Font)
{
	FontCur = Font;
	
	switch(Font)
	{
	    case FONT16:
	        AsciiImg = &ASCII_SongTi_8_16[0][0];
	        FontSize = 16 * 2;
	        break;
	    case FONT24:
	        AsciiImg = &ASCII_SongTi_12_24[0][0];
	        FontSize = 24 * 3;
	        break;
	    case FONT32:
	        AsciiImg = &ASCII_SongTi_16_32[0][0];
	        FontSize = 32 * 4;
	        break;
	    default:
	        AsciiImg = &ASCII_SongTi_8_16[0][0];
	        FontCur  = FONT16;
	        FontSize = 16 * 2;
	        break;
	            
	}
}

/******************************************************************************
 * Waiting LCD untile it is not busy
 * @param Content
 *****************************************************************************/
void LCD19264_Busy()
{
	uint16_t TimeOutSt;
	
    LCD19264_RS(0);           //read status reg
    LCD19264_RW(1);
    LCD19264_Com_DEL();
    LCD19264_E(1);
	LCD19264_DATA_IN();
    LCD19264_Read_DEL();
	
#if LCD19264_ENABLE > 0 
	if(WaitTimeOutCnt < LCD_WAIT_TIME_OUT_LIMIT)
	{
		TimeOutSt = 0;
		while(LCD19264_BUSY() != 0)
		{
			if((TimeOutSt ++) > LCD_WAIT_TIME_OUT_CYCLE_CNT)
				break;
		}
		
		if(TimeOutSt > LCD_WAIT_TIME_OUT_CYCLE_CNT)
			WaitTimeOutCnt ++;
		else
			WaitTimeOutCnt = 0;
	}
#endif
	
    LCD19264_E(0);
}

/******************************************************************************
 * Write command to lcd
 * @param Content
 *****************************************************************************/
void LCD19264_ComW(unsigned char Command)
{
    LCD19264_Busy();
    LCD19264_RS(0);         //com
    LCD19264_RW(0);        //write
    LCD19264_DATA_OUT();
    LCD19264_DATA_Write(Command);
    LCD19264_Com_DEL();
    LCD19264_E(1);
    LCD19264_E_DEL();
    LCD19264_E(0);
}

/******************************************************************************
 * Write data to lcd
 * @param Content
 *****************************************************************************/
void LCD19264_DataW(unsigned char Data)
{
    LCD19264_Busy();
    LCD19264_RS(1);        //data
    LCD19264_RW(0);        //write
    LCD19264_DATA_OUT();
    LCD19264_DATA_Write(Data);
    LCD19264_Com_DEL();
    LCD19264_E(1);
    LCD19264_E_DEL();
    LCD19264_E(0);
}

/******************************************************************************
 * Write command to CS1
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom_CS1(unsigned char Content)
{
    LCD19264_CS1_Enable();
    LCD19264_CS2_Disable();
    LCD19264_CS3_Disable();

    LCD19264_ComW(Content);
}

/******************************************************************************
 * Write command to CS2
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom_CS2(unsigned char Content)
{
    LCD19264_CS1_Disable();
    LCD19264_CS2_Enable();
    LCD19264_CS3_Disable();
	
	LCD19264_ComW(Content);
}

/******************************************************************************
 * Write command to CS3
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom_CS3(unsigned char Content)
{
    LCD19264_CS1_Disable();
    LCD19264_CS2_Disable();
    LCD19264_CS3_Enable();
	
    LCD19264_ComW(Content);
}

/******************************************************************************
 * Write command to CS1/CS2/CS3
 * @param Content
 *****************************************************************************/
void LCD19264_WriteCom(unsigned char U,unsigned char Content)
{
    if(U == 0)
    {
		LCD19264_WriteCom_CS1(Content);
    }
    else if(U == 1)
    {
		LCD19264_WriteCom_CS2(Content);

    }
    else
    {
		LCD19264_WriteCom_CS3(Content);
    }
}


/******************************************************************************
 * Clear the screen
 * @param Content
 *****************************************************************************/
void LCD19264ScreenFill(unsigned char Data)
{
    unsigned char Col,Page;
	unsigned char LMR;

	for(LMR = 0; LMR<LCD_CS; LMR++)
	{	
		for(Page=0;Page<8;Page++)
		{
			LCD19264_WriteCom(LMR,0xB8+Page);
			LCD19264_WriteCom(LMR,0x40);
			for(Col=0;Col<64;Col++)
			{
				LCD19264_Sub_DEL();
				LCD19264_DataW(Data);
			}
		}
	}
    __nop();
}


/******************************************************************************
 * Set start line on screen
 * @param Content
 *****************************************************************************/
void LCD19264StartLineAll(unsigned char Line)
{
    LCD19264_WriteCom_CS1( 0xC0 + Line);
    LCD19264_WriteCom_CS2( 0xC0 + Line);
    LCD19264_WriteCom_CS3( 0xC0 + Line);
}

/******************************************************************************
 * Set start line on screen
 * @param Content
 *****************************************************************************/
void LCD19264StartLine(unsigned char U,unsigned char Line)
{
    switch(U)
    {
        case 0:
			LCD19264_WriteCom_CS1( 0xC0 + Line);
			break;
        case 1:
			LCD19264_WriteCom_CS2( 0xC0 + Line);
			break;
        case 2:
			LCD19264_WriteCom_CS3( 0xC0 + Line);
			break;
        default:
			break;
    }
}

/******************************************************************************
 * DispAnti
 * @param Content
 *****************************************************************************/
void LCD19264DispAnti()
{
	LCDDataMask = 0xFF;
}

void LCD19264DispHalfEn(void)
{
	bHalfSpace = 1;
}

void LCD19264DispHalfDis(void)
{
	bHalfSpace = 0;
}

/******************************************************************************
 * Disp Normal
 * @param Content
 *****************************************************************************/
void LCD19264DispNormal()
{
	LCDDataMask = 0x00;
}

void LCD19264PutCh(unsigned char Page,unsigned char Col,unsigned char DispCh)
{
    unsigned char LMR;              // left /middle /right screen
    unsigned char ColCounter;
    unsigned char FontLineSize;
    unsigned char LMRChange;
    unsigned char PageIndex;
    
	FontLineSize = (FontSize / FontCur) >> 1;
    if(Page > (8-FontCur))  return;
    if(Col > (LCD_LINE_SIZE - FontLineSize)) return;
        
    LMR = Col >> 6;
    ColCounter = 0;  
	LMRChange  = 0;
	
    PageIndex = 0;
    while(PageIndex < FontCur)
    {
        LCD19264_WriteCom(LMR,0xB8 + Page + PageIndex);                   // Set the first Page of the char
        LCD19264_WriteCom(LMR,0x40 + (Col & 0x3F));                       // Set the start Col of the char     
        LMRChange = 0;   
        
        while(ColCounter < FontLineSize)                                  //Write the first page of the char
        {
            if((Col + ColCounter) >= (64 * (LMR + 1)))
            {
                if(LMRChange == 0)
                {
                    LMRChange = 1;
                    LCD19264_WriteCom(LMR+1,0xB8 + Page + PageIndex);     // Set the first Page of the char
                    LCD19264_WriteCom(LMR+1,0x40);                        // Set the start Col of the char 
                }               
            }
			
            LCD19264_DataW( AsciiImg[((DispCh - 0x20) * (FontSize >> 1)) + (PageIndex * FontLineSize + ( ColCounter ++ ))] ^ LCDDataMask );
        }
        PageIndex ++;
        ColCounter = 0;
    }
}

/******************************************************************************
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LCD19264Puts(unsigned char Page,unsigned char Col,unsigned char *DispStr)
{
    while(*DispStr > 0x19)
    {
        LCD19264PutCh(Page,Col, *DispStr);
		
		if(bHalfSpace && ((' ' == *DispStr) || ('.' == *DispStr)))
			Col += (FontSize / FontCur) >> 2;
		else
			Col += (FontSize / FontCur) >> 1;
		
        if(Col > LCD_LINE_SIZE)
            break;  
		
		DispStr ++;
    }  
}

/******************************************************************************
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LCD19264PutCN(unsigned char Page,unsigned char Col,const unsigned char *pCN)
{
    unsigned char LMR;              // left /middle /right screen
    unsigned char ColCounter;
    unsigned char FontLineSize;
    unsigned char LMRChange;
    unsigned char PageIndex;
    
	FontLineSize = FontSize / FontCur;
    if(Page > (8-FontCur))  return;
    if(Col > (LCD_LINE_SIZE - FontLineSize)) return;
        
    LMR = Col >> 6;
    ColCounter = 0;  
	LMRChange  = 0;
	
    PageIndex = 0;
    while(PageIndex < FontCur)
    {
        LCD19264_WriteCom(LMR,0xB8 + Page + PageIndex);                   // Set the first Page of the char
        LCD19264_WriteCom(LMR,0x40 + (Col & 0x3F));                       // Set the start Col of the char     
        LMRChange = 0;   
        
        while(ColCounter < FontLineSize)                                  //Write the first page of the char
        {
            if((Col + ColCounter) >= (64 * (LMR + 1)))
            {
                if(LMRChange == 0)
                {
                    LMRChange = 1;
                    LCD19264_WriteCom(LMR+1,0xB8 + Page + PageIndex);     // Set the first Page of the char
                    LCD19264_WriteCom(LMR+1,0x40);                        // Set the start Col of the char 
                }               
            }
            LCD19264_DataW( pCN[PageIndex * FontLineSize + ( ColCounter ++ )] ^ LCDDataMask );
        }
        PageIndex ++;
        ColCounter = 0;
    }
}

/******************************************************************************
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LCD19264PutCNs(unsigned char Page,unsigned char Col,unsigned char Amount, const unsigned char *pCN)
{
    while(Amount--)
    {
        LCD19264PutCN(Page,Col,pCN);  
        pCN += FontSize;  
        Col += (FontSize / FontCur);  
        if(Col > LCD_LINE_SIZE)
           break;        
    }
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void LCD19264LineDisp(unsigned char Page ,unsigned char Col,unsigned char Wide,const unsigned char *pImg)
{
    unsigned char StartLMR ,RecLMR;
    unsigned char ColRel;
    unsigned char WideUnprinted;
	unsigned char Index;

    StartLMR = 0;
    ColRel = Col;
    while(ColRel > 63)
    {
        ColRel -= 64;
        StartLMR ++;
    }

    RecLMR = StartLMR;
    WideUnprinted = Wide;
	Index = 0;
    /************************************Wriet Data****************************/
	while(RecLMR < LCD_CS)                           //Screen is not over
	{
		LCD19264_WriteCom(RecLMR,0xB8 + Page);       //Set Page
		LCD19264_WriteCom(RecLMR,0x40 + ColRel);     //Set Start Col

		while((ColRel++ < 64))
		{
			LCD19264_DataW(pImg[Index ++] ^ LCDDataMask );
			if((--WideUnprinted) == 0 ) return;
		}
		RecLMR ++;
		ColRel = 0;
	}
    /*************************************************************************/
}

/****************************************************************************
 * The High must be a multiple of 8.
 ****************************************************************************/
void LCD19264ImgDisp(unsigned char StartPage ,unsigned char StartCol ,\
unsigned char Wide ,unsigned char PageHigh ,const unsigned char *pImg)
{
    while( (PageHigh > 0 ) && (StartPage < 8) )
    {
        LCD19264LineDisp(StartPage,StartCol,Wide,pImg);
        pImg += Wide;
        PageHigh --;
        StartPage ++;
    }
}

